<script setup>
import request from '@/utils/request'
import { tip } from '@/utils/uni-api-wrap/toast'
import { isEmpty } from 'lodash'
import {reactive, onBeforeMount, watch} from 'vue'
import {isJsonObjectsEqual, lockWrap} from '@/utils/utils'
import to from 'await-to-js'

const initParams = (options = {}) => ({
  page: 1,
  limit: 10,
  loading: false,
  has_next: true,
  next: null, // 下一页游标起点标志，参考https://blog.csdn.net/jam_yin/article/details/134921294
  total: 0,
  list: [],
  firstLoading: true,
  triggered: false,
  ...options,
});

const props = defineProps({
  params: {
    type: Object,
    default: () => ({}),
  },
  height: {
    type: String,
    default: '100vh',
  },
  url: String,
  method: {
    type: String,
    default: 'get',
  },
  page: {
    type: Number,
    default: 1,
  },
  limit: {
    type: Number,
    default: 10,
  },
  listMap: {
    type: Function,
    default: item => item,
  },
  lazy: {
    type: Boolean,
    default: false,
  },
  refresherTriggered: {
    type: Boolean,
    default: true,
  },
})

const emit = defineEmits(['loaded'])

const state = reactive(initParams({
  page: props.page,
  limit: props.limit,
}))

const params = ref(props.params)

const fetchLoading = lockWrap(
  async function() {
    if (state.loading) return;
    if (!state.has_next) return;
    if (!props.url) return;
    state.loading = true;
    const { next, limit } = state
    const page = { size: limit }
    if (next) {
      page.next = next
    }
    const [err, result] = await to(request[props.method](props.url, { ...params.value, page }))
    state.firstLoading = false
    if (err) {
      state.has_next = false
      return
    }
    if (result?.code !== 0) {
      state.has_next = false
      return tip(result?.msg || '请求失败，请重试')
    }
    const { data } = result
    state.total = data.total
    state.has_next = !!data.next
    state.next = data.next
    if (isEmpty(state.list)) {
      state.list = data.list.map(props.listMap)
    } else {
      state.list = [...state.list, ...data.list].map(props.listMap)
    }
    emit('loaded', state.list)
    state.loading = false
  }
)

const next = async function() {
  state.page++;
  await fetchLoading();
}

const previous = async function() {
  state.page--;
  await fetchLoading();
}

const skip = async function (page, limit) {
  Object.assign(state, initParams({
    page: props.page,
    limit: props.limit,
  }))
  await fetchLoading();
}

const refresh = async function () {
  await skip(1, state.limit);
  setTimeout(() => (state.triggered = false), 600);
}

const onPulling = function () {
  if (state.triggered) return;
  state.triggered = true;
}

const getList = function () {
  return state.list
}

onBeforeMount(() => {
  if (!props.url) throw Error("请求地址是必须的");
  !props.lazy && fetchLoading();
})

watch(props, newProps => {
  params.value = newProps.params
  if (!props.lazy && newProps.limit !== state.limit) {
    refresh()
  }
  if (!props.lazy && newProps.page !== state.page) {
    refresh()
  }
})

watch(params, (newParams, oldParams) => {
  if (!props.lazy && !isJsonObjectsEqual(newParams, oldParams)) {
    refresh()
  }
})

defineExpose({
  fetchLoading,
  next,
  previous,
  skip,
  refresh,
  getList
})

</script>

<template>
  <scroll-view
    scroll-y
    class="page-list-container"
    :refresher-enabled="props.refresherTriggered"
    :refresher-triggered="state.triggered"
    :style="{ height: props.height }"
    @refresherpulling="onPulling"
    @refresherrefresh="refresh"
    @scrolltolower="next"
  >
    <view class="list-inner__contianer">
      <slot
        :loading="state.loading"
        :has_next="state.has_next"
        :list="state.list"
        :firstLoading="state.firstLoading"
      ></slot>
    </view>
  </scroll-view>
</template>

<style lang="scss" scoped>
.page-list-container {
  width: 100%;
}
.list-inner__contianer {
  @include marginOverlap;
}
</style>
